<script setup lang='ts'>
import { SiderMenuEnum, RoleEnum } from '@/models/@types';
import { appRef } from '@/models/app.ref';
import { onMounted, ref, watch } from 'vue';
import { Transformer } from 'markmap-lib';
import { Markmap } from 'markmap-view';
import { MindMapInfo } from '@/models/mind-map';
import { GeneratePromptReq, promptService } from '@/services/prompt.service';
import Sider from '@/components/chat/Sider.vue'
import Explanation from '@/components/Explanation.vue'
import { covertSVG2Image, formatTime, getUuid, isWeChatBrowser } from '@/utils/common.methods';
import { Message } from '@arco-design/web-vue';

const menus = ref([]);
const currMenu = ref({} as MindMapInfo);


const mindMap = ref(null)
const svgRef = ref(null)
const svgExportRef = ref(null)
watch(() => currMenu?.value?.value, () => {
  // 监听输入变化更新思维导图
  renderMindMap()
})
onMounted(async () => {
  await init();
  setTimeout(() => {
    // 初始化markmap思维导图
    mindMap.value = Markmap.create(svgRef.value);
    mindMapExport = Markmap.create(svgExportRef.value);
    // 更新思维导图渲染
    renderMindMap();
  }, 300);
})


const transformer = new Transformer();
function renderMindMap() {
  const { root } = transformer.transform(currMenu.value.value);
  mindMap.value?.setData(root);
  mindMap.value?.fit();
}


async function init(): Promise<void> {
  menus.value = await appRef.mindMap.menus();
  currMenu.value = appRef.mindMap?.currMenu;
  const { originalWords, value } = currMenu.value;
  if (originalWords && !value) {
    onGenerate();
  }
}

async function onAdd(e: { type: SiderMenuEnum, parent?: string }): Promise<void> {
  const { type, parent } = e
  await appRef.mindMap.add(type, parent);
  init();
}

async function onUpdate(enmus: MindMapInfo[]): Promise<void> {
  await appRef.mindMap.updateEmuse(enmus)
  init();
}
async function onItem(uid: string): Promise<void> {
  console.log('onItem', uid);
  await appRef.mindMap.setCurrKey(uid);
  init();
}
async function onEdit(param: { uid: string, label: string }): Promise<void> {
  const { uid, label } = param;
  await appRef.mindMap.edit(uid, label);
}


const controllerKey = getUuid();

const isSend = ref(false);
function onGenerate(): void {
  const { originalWords, uid } = currMenu.value;
  appRef.mindMap.edit(uid, originalWords?.slice(0, 10))
  currMenu.value.value = '';
  isSend.value = true;
  const message: GeneratePromptReq[] = [
    {
      role: RoleEnum.System,
      content: `You are an expert at producing complex, long text and creating Markdown format compatible brain maps. The created brain map should be a complete multilevel structure but each sentence should be concise and accurate.

Your Output Format of Each Level:
# Fisrt Level 
## Second Level 
### Third Level 
#### Fourth Level 
- Fifth Level 

Other requirements:
1. Your answer should exclude all content that is not in Markdown format and exclude code block form
2. If my input is too long, please give a complete brain map structure according to what I input, but each sentence should be concise and accurate.
3. If my input is too short and only contains the theme and requirements, please create according to the theme and requirements given by me and give a corresponding complete brain map structure, but each sentence should be concise and accurate.
4. Brain map to at least the third level! Brain map to at least the third level! Brain map to at least the third level!
                `
    },
    {
      role: RoleEnum.User,
      content:
        originalWords,
    },
  ];
  promptService.message(controllerKey, message, chunk, end);
}


function onStopSend(): void {
  currMenu.value.value = '';
  promptService.stopStream(controllerKey);
  isSend.value = false;
}

function chunk(text: string): void {
  currMenu.value.value += text;
}

function end(text: string): void {
  currMenu.value.value = text;
  isSend.value = false;
}



let mindMapExport = null;
const exportBase64 = ref('');
const isExport = ref(false);
async function exportImage(): Promise<void> {
  isExport.value = true;
  const { root } = transformer.transform(currMenu.value.value);
  mindMapExport?.setData(root);
  mindMapExport?.fit();
  setTimeout(async () => {
    const mindMap = document.querySelector('.mind-map-export');
    const { clientWidth, clientHeight } = mindMap;
    const base64 = await covertSVG2Image(mindMap, 'zaiwen.top' + formatTime(new Date(), 'yyyy/MM/dd hh:mm:ss'), clientWidth, clientHeight);
    if (isWeChatBrowser()) {
      exportBase64.value = base64;
      Message.warning("长按图片保存至本地");
    } else {
      Message.success("图片已保存至本地");
    }
    isExport.value = false;
  }, 1000);
}

</script>
<template>
  <div class="index">
    <a-layout style="height: 100%;">

      <a-layout>
        <div class="main flex-c" v-if="currMenu?.uid">
          <div class="bottom flex">
            <a-textarea class="textarea" v-model="currMenu.originalWords" @keydown.ctrl.enter="onGenerate"
              placeholder="随便输入一句话，我帮你总结为思维导图" :disabled="isSend" />
            <div class="btns flex-c">
              <a-button class="send" type="primary" :disabled="!currMenu.originalWords" @click.stop="onGenerate"
                :loading="isSend">
                <template #icon> <icon-send /> </template>
                <template #default>发送</template>
              </a-button>
              <a-button class="send" type="primary" :disabled="!currMenu.value" @click.stop="exportImage"
                :loading="isSend || isExport">
                <template #icon><icon-download /> </template>
                <template #default>保存</template>
              </a-button>
              <a-button class="stopSendbtn" status="warning" type="primary" v-if="isSend" @click="onStopSend">
                <template #icon> <icon-close /> </template>
                <span>停止响应</span>
              </a-button>
            </div>
          </div>
          <div class="content flex">
            <a-textarea class="textarea" v-model="currMenu.value" @keydown.ctrl.enter="onGenerate"
              placeholder="您可以在这里修改您的思维导图" :disabled="isSend" />
            <svg class="mind-map" ref="svgRef"></svg>
            <svg class="mind-map mind-map-export" ref="svgExportRef"></svg>
          </div>
        </div>
        <Explanation v-else />
      </a-layout>
      <Sider :menus="menus" :default-uid="currMenu?.uid" @add="onAdd"
        @clear="async () => { await appRef.mindMap.clear(); init() }" @click-item="onItem"
        @edit="($event) => { onEdit($event); init() }"
        @remove="async ($event) => { await appRef.mindMap.remove($event); init() }"
        @update="($event) => onUpdate($event)" />
    </a-layout>
  </div>
  <a-image-preview :src="exportBase64" :visible="!!exportBase64" @close="exportBase64 = ''" />
</template>
<style lang='scss' scoped>
.index {
  height: 100%;

  .main {
    width: 95%;
    height: 100%;
    justify-content: center;
    max-width: 1270px;
    margin: 0 auto;
    padding: 20px 0;

    .content {
      width: 100%;
      margin: 0px;
      flex: 1;
      justify-content: center;

      .textarea {
        width: 30%;
        height: 100%;
      }

      .mind-map {
        width: 100%;
        height: 100%;
        margin-left: 10px;
        background: var(--color-neutral-2);
      }

      .mind-map-export {
        position: fixed;
        left: 9999px;
        width: 2000px;
        height: 2000px;
      }
    }

    @media screen and (max-width: 1200px) {
      .content {
        flex-direction: column;

        .textarea {
          width: 100%;
          height: 30%;
        }

        .mind-map {
          margin-left: 0;
          margin-top: 10px;
        }
      }
    }



    .bottom {
      width: 100%;
      height: 13%;
      margin-bottom: 10px;
      position: relative;

      .textarea {
        height: 100%;

        ::v-deep .arco-textarea {
          padding-right: 100px;
        }
      }

      .btns {
        height: 100%;
        position: absolute;
        right: 10px;
        top: 10px;

        .send {
          margin-bottom: 10px;
        }
      }
    }
  }
}
</style>
